# -*- coding: utf-8 -*-
import asyncio
import random
import time
import json
import os
import logging
import re
from typing import Dict, List, Optional, Set, Tuple, Callable, Any, Union
from datetime import datetime
from enum import Enum

class GameState(Enum):
    WAITING = "waiting"
    PLAYING = "playing"
    FINISHED = "finished"

class NumberBombPlugin(JCbotPlugin):
    def __init__(self, bot_instance):
        self.bot = bot_instance
        self.plugin_name = "NumberBomb"
        self.version = "1.0.0"
        self.description = "数字炸弹小游戏插件"
        
        # 初始化游戏状态
        self.game_state = GameState.FINISHED
        self.target_number = None
        self.players = []
        self.current_player_index = 0
        self.min_range = 0
        self.max_range = 100
        self.max_players = 5
        self.creator_id = None
        self.round_count = 0
        self.start_time = 0
        self.timeout_seconds = 30
        self.timeout_tasks = {}
        self.active_group = None
        
        # 积分系统
        self.scores = {}
        self.score_records = {}
        
        # 游戏记录文件路径
        self.config_dir = os.path.join(self.bot.base_dir, "Configs", "NumberBomb")
        self.scores_file = os.path.join(self.config_dir, "scores.json")
        self.records_file = os.path.join(self.config_dir, "records.json")
        
        # 加载历史数据
        self.load_scores()
        self.load_records()
        
        # 设置日志
        self.logger = logging.getLogger(f"JCbot.{self.plugin_name}")

    def register(self):
        self.logger.info("数字炸弹小游戏插件已加载")
        
    def get_handlers(self):
        return {
            "create_game": self.handle_create_game,
            "join_game": self.handle_join_game,
            "start_game": self.handle_start_game,
            "guess": self.handle_guess,
            "stop_game": self.handle_stop_game,
            "show_leaderboard": self.handle_show_leaderboard,
            "show_my_records": self.handle_show_my_records,
            "set_game_params": self.handle_set_game_params
        }
        
    def get_commands_config(self):
        return {
            "创建游戏": {
                "description": "创建数字炸弹游戏",
                "handler": "create_game",
                "permission": 1,
                "fuzzy_match": False,
                "group": "数字炸弹"
            },
            "加入游戏": {
                "description": "加入数字炸弹游戏",
                "handler": "join_game",
                "permission": 1,
                "fuzzy_match": False,
                "group": "数字炸弹"
            },
            "开始游戏": {
                "description": "开始数字炸弹游戏",
                "handler": "start_game",
                "permission": 1,
                "fuzzy_match": False,
                "group": "数字炸弹"
            },
            "猜": {
                "description": "猜数字",
                "handler": "guess",
                "permission": 1,
                "fuzzy_match": True,
                "group": "数字炸弹"
            },
            "终止游戏": {
                "description": "终止当前游戏（管理员）",
                "handler": "stop_game",
                "permission": 2,
                "fuzzy_match": False,
                "group": "数字炸弹"
            },
            "游戏排行榜": {
                "description": "显示游戏积分排行榜",
                "handler": "show_leaderboard",
                "permission": 1,
                "fuzzy_match": False,
                "group": "数字炸弹"
            },
            "我的记录": {
                "description": "显示我的游戏记录",
                "handler": "show_my_records",
                "permission": 1,
                "fuzzy_match": False,
                "group": "数字炸弹"
            },
            "设置游戏参数": {
                "description": "设置游戏参数（管理员）",
                "handler": "set_game_params",
                "permission": 2,
                "fuzzy_match": False,
                "group": "数字炸弹"
            }
        }
    
    def load_scores(self):
        """加载积分数据"""
        try:
            if os.path.exists(self.scores_file):
                with open(self.scores_file, 'r', encoding='utf-8') as f:
                    self.scores = json.load(f)
        except Exception as e:
            self.logger.error(f"加载积分数据失败: {e}")
            self.scores = {}
    
    def save_scores(self):
        """保存积分数据"""
        try:
            # 确保目录存在
            os.makedirs(self.config_dir, exist_ok=True)
            
            with open(self.scores_file, 'w', encoding='utf-8') as f:
                json.dump(self.scores, f, ensure_ascii=False, indent=4)
        except Exception as e:
            self.logger.error(f"保存积分数据失败: {e}")
    
    def load_records(self):
        """加载记录数据"""
        try:
            if os.path.exists(self.records_file):
                with open(self.records_file, 'r', encoding='utf-8') as f:
                    self.score_records = json.load(f)
        except Exception as e:
            self.logger.error(f"加载记录数据失败: {e}")
            self.score_records = {}
    
    def save_records(self):
        """保存记录数据"""
        try:
            os.makedirs(self.config_dir, exist_ok=True)
            
            with open(self.records_file, 'w', encoding='utf-8') as f:
                json.dump(self.score_records, f, ensure_ascii=False, indent=4)
        except Exception as e:
            self.logger.error(f"保存记录数据失败: {e}")
    
    def add_score_record(self, user_id: int, reason: str, change: int):
        """添加积分记录"""
        if user_id not in self.score_records:
            self.score_records[user_id] = []
        
        record = {
            "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
            "reason": reason,
            "change": change
        }
        
        self.score_records[user_id].append(record)
        
        # 限制记录数量
        if len(self.score_records[user_id]) > 100:
            self.score_records[user_id] = self.score_records[user_id][-100:]
        
        self.save_records()
    
    def update_score(self, user_id: int, change: int, reason: str):
        """更新积分并添加记录"""
        if user_id not in self.scores:
            self.scores[user_id] = 0
        
        self.scores[user_id] += change
        self.add_score_record(user_id, reason, change)
        self.save_scores()
    
    async def get_user_name(self, group_id: int, user_id: int) -> str:
        """获取用户名"""
        try:
            # 尝试从主程序获取用户信息
            if hasattr(self.bot, 'get_user_name'):
                return await self.bot.get_user_name(group_id, user_id)
            
            # 如果主程序没有提供，使用备用方法
            return f"玩家{user_id}"
        except Exception as e:
            self.logger.error(f"获取用户名失败: {e}")
            return str(user_id)
    
    async def send_group_message(self, group_id: int, message: str):
        """发送群消息"""
        try:
            await self.bot.send_group_message(group_id, message)
        except Exception as e:
            self.logger.error(f"发送群消息失败: {e}")
    
    async def handle_create_game(self, group_id: int, user_id: int, command: str, 
                               param: str, options: Dict = None) -> str:
        """处理创建游戏命令"""
        if self.game_state != GameState.FINISHED:
            return "当前已有游戏在进行中，请先等待游戏结束或使用'终止游戏'命令"
        
        # 解析参数
        params = param.split()
        min_range = 0
        max_range = 100
        max_players = 5
        timeout_seconds = 30
        
        if len(params) >= 2:
            try:
                min_range = int(params[0])
                max_range = int(params[1])
                if len(params) >= 3:
                    max_players = int(params[2])
                if len(params) >= 4:
                    timeout_seconds = int(params[3])
            except ValueError:
                return "参数格式错误，请使用: 创建游戏 [最小数字] [最大数字] [最大玩家数] [超时时间]"
        
        # 验证参数
        if min_range >= max_range:
            return "最小数字必须小于最大数字"
        
        if max_players < 2:
            return "最大玩家数必须至少为2"
        
        if timeout_seconds < 10:
            return "超时时间必须至少为10秒"
        
        # 初始化游戏
        self.game_state = GameState.WAITING
        self.players = [user_id]
        self.creator_id = user_id
        self.active_group = group_id
        self.min_range = min_range
        self.max_range = max_range
        self.max_players = max_players
        self.timeout_seconds = timeout_seconds
        self.round_count = 0
        self.start_time = time.time()
        
        # 发送创建成功消息
        await self.send_group_message(group_id, 
            f"游戏已创建！数字范围: {self.min_range}-{self.max_range}\n"
            f"最大玩家数: {self.max_players}\n"
            f"超时时间: {self.timeout_seconds}秒\n"
            f"使用'加入游戏'命令加入游戏，使用'开始游戏'命令开始游戏")
        
        return "游戏创建成功"
    
    async def handle_join_game(self, group_id: int, user_id: int, command: str, 
                             param: str, options: Dict = None) -> str:
        """处理加入游戏命令"""
        if self.game_state != GameState.WAITING:
            return "当前没有等待加入的游戏"
        
        if group_id != self.active_group:
            return "游戏不在当前群组中"
        
        if user_id in self.players:
            return "您已经加入了游戏"
        
        if len(self.players) >= self.max_players:
            return "游戏人数已满"
        
        # 添加玩家
        self.players.append(user_id)
        
        # 发送加入成功消息
        player_name = await self.get_user_name(group_id, user_id)
        await self.send_group_message(group_id, f"{player_name} 加入了游戏！当前玩家数: {len(self.players)}/{self.max_players}")
        
        return "加入游戏成功"
    
    async def handle_start_game(self, group_id: int, user_id: int, command: str, 
                              param: str, options: Dict = None) -> str:
        """处理开始游戏命令"""
        if self.game_state != GameState.WAITING:
            return "游戏不在等待状态"
        
        if group_id != self.active_group:
            return "游戏不在当前群组中"
        
        if user_id != self.creator_id:
            return "只有游戏创建者可以开始游戏"
        
        if len(self.players) < 2:
            return "至少需要2名玩家才能开始游戏"
        
        # 生成目标数字
        self.target_number = random.randint(self.min_range, self.max_range)
        self.game_state = GameState.PLAYING
        self.current_player_index = 0
        self.round_count += 1
        
        # 发送游戏开始消息
        current_player_name = await self.get_user_name(group_id, self.players[self.current_player_index])
        await self.send_group_message(group_id, 
            f"游戏开始！第{self.round_count}轮\n"
            f"数字范围: {self.min_range}-{self.max_range}\n"
            f"当前玩家: {current_player_name}\n"
            f"请使用'猜 [数字]'命令猜测数字")
        
        # 设置超时计时器
        self.set_timeout_timer(group_id)
        
        return "游戏开始成功"
    
    async def handle_guess(self, group_id: int, user_id: int, command: str, 
                         param: str, options: Dict = None) -> str:
        """处理猜数字命令"""
        if self.game_state != GameState.PLAYING:
            return "当前没有进行中的游戏"
        
        if group_id != self.active_group:
            return "游戏不在当前群组中"
        
        if user_id != self.players[self.current_player_index]:
            current_player_name = await self.get_user_name(group_id, self.players[self.current_player_index])
            return f"现在不是您的回合，请等待玩家 {current_player_name} 的回合"
        
        if not param:
            return "请输入要猜的数字"
        
        # 尝试提取数字
        match = re.search(r'(\d+)', param)
        if not match:
            return "请输入有效的数字"
        
        try:
            guess = int(match.group(1))
        except ValueError:
            return "请输入有效的数字"
        
        # 严格验证数字范围
        if guess < self.min_range or guess > self.max_range:
            return f"数字必须在 {self.min_range} 到 {self.max_range} 之间"
        
        # 处理数字猜测
        await self.process_guess(group_id, user_id, guess)
        return ""
    
    async def process_guess(self, group_id: int, user_id: int, guess: int):
        """处理数字猜测"""
        # 取消超时计时器
        self.cancel_timeout_timer()
        
        # 检查猜测是否正确
        if guess == self.target_number:
            await self.handle_correct_guess(group_id, user_id, guess)
        else:
            await self.handle_incorrect_guess(group_id, user_id, guess)
    
    async def handle_correct_guess(self, group_id: int, user_id: int, guess: int):
        """处理正确猜测"""
        # 更新积分
        self.update_score(user_id, 1, f"第{self.round_count}轮猜中数字 {guess}")
        
        # 发送猜中消息
        player_name = await self.get_user_name(group_id, user_id)
        await self.send_group_message(group_id, 
            f"🎉 {player_name} 猜中了数字 {guess}！\n"
            f"获得 1 积分！\n"
            f"游戏结束，开始新的一轮")
        
        # 开始新一轮
        await self.start_new_round(group_id)
    
    async def handle_incorrect_guess(self, group_id: int, user_id: int, guess: int):
        """处理错误猜测"""
        # 更新范围
        if guess < self.target_number:
            self.min_range = guess + 1
            hint = "更大"
        else:
            self.max_range = guess - 1
            hint = "更小"
        
        # 发送提示消息
        player_name = await self.get_user_name(group_id, user_id)
        await self.send_group_message(group_id, 
            f"{player_name} 猜测: {guess} ✗\n"
            f"提示: 数字 {hint}\n"
            f"新范围: {self.min_range}-{self.max_range}")
        
        # 切换到下一个玩家
        self.current_player_index = (self.current_player_index + 1) % len(self.players)
        next_player_name = await self.get_user_name(group_id, self.players[self.current_player_index])
        
        await self.send_group_message(group_id, 
            f"轮到 {next_player_name} 猜测\n"
            f"请使用'猜 [数字]'命令猜测数字")
        
        # 设置超时计时器
        self.set_timeout_timer(group_id)
    
    async def handle_stop_game(self, group_id: int, user_id: int, command: str, 
                             param: str, options: Dict = None) -> str:
        """处理终止游戏命令"""
        if self.game_state == GameState.FINISHED:
            return "当前没有进行中的游戏"
        
        if group_id != self.active_group:
            return "游戏不在当前群组中"
        
        # 检查权限
        if user_id != self.creator_id and self.bot.get_user_permission(user_id) < 2:
            return "只有游戏创建者或管理员可以终止游戏"
        
        # 终止游戏
        await self.end_game(group_id, "游戏被终止")
        
        return "游戏已终止"
    
    async def handle_show_leaderboard(self, group_id: int, user_id: int, command: str, 
                                    param: str, options: Dict = None) -> str:
        """处理显示排行榜命令"""
        if not self.scores:
            return "暂无积分记录"
        
        # 排序积分
        sorted_scores = sorted(self.scores.items(), key=lambda x: x[1], reverse=True)
        
        # 构建排行榜消息
        leaderboard = "游戏积分排行榜:\n"
        for i, (player_id, score) in enumerate(sorted_scores[:10], 1):
            player_name = await self.get_user_name(group_id, player_id)
            leaderboard += f"{i}. {player_name}: {score}分\n"
        
        return leaderboard
    
    async def handle_show_my_records(self, group_id: int, user_id: int, command: str, 
                                   param: str, options: Dict = None) -> str:
        """处理显示我的记录命令"""
        if user_id not in self.score_records:
            return "您暂无游戏记录"
        
        records = self.score_records[user_id]
        max_display = 10
        
        # 构建记录消息
        record_msg = f"您的最近{min(len(records), max_display)}条记录:\n"
        for record in records[-max_display:]:
            record_msg += f"{record['time']}: {record['reason']} ({record['change']:+}分)\n"
        
        total_score = self.scores.get(user_id, 0)
        record_msg += f"\n总积分: {total_score}分"
        
        return record_msg
    
    async def handle_set_game_params(self, group_id: int, user_id: int, command: str, 
                                   param: str, options: Dict = None) -> str:
        """处理设置游戏参数命令"""
        # 检查权限
        if self.bot.get_user_permission(user_id) < 2:
            return "权限不足，需要管理员权限"
        
        # 解析参数
        params = param.split()
        if len(params) < 2:
            return "参数不足，请使用: 设置游戏参数 [参数名] [值]"
        
        param_name = params[0]
        param_value = params[1]
        
        # 设置参数
        try:
            if param_name == "min_range":
                self.min_range = int(param_value)
            elif param_name == "max_range":
                self.max_range = int(param_value)
            elif param_name == "max_players":
                self.max_players = int(param_value)
            elif param_name == "timeout":
                self.timeout_seconds = int(param_value)
            else:
                return f"未知参数: {param_name}"
        except ValueError:
            return "参数值必须是整数"
        
        return f"参数 {param_name} 已设置为 {param_value}"
    
    async def start_new_round(self, group_id: int):
        """开始新一轮游戏"""
        # 重置范围
        self.min_range = 0
        self.max_range = 100
        
        # 生成新数字
        self.target_number = random.randint(self.min_range, self.max_range)
        self.round_count += 1
        self.current_player_index = 0
        
        # 发送新一轮开始消息
        first_player_name = await self.get_user_name(group_id, self.players[self.current_player_index])
        await self.send_group_message(group_id, 
            f"第{self.round_count}轮开始！\n"
            f"数字范围: {self.min_range}-{self.max_range}\n"
            f"当前玩家: {first_player_name}\n"
            f"请使用'猜 [数字]'命令猜测数字")
        
        # 设置超时计时器
        self.set_timeout_timer(group_id)
    
    async def end_game(self, group_id: int, reason: str):
        """结束游戏"""
        # 发送结束消息
        await self.send_group_message(group_id, f"游戏结束: {reason}")
        
        # 重置游戏状态
        self.game_state = GameState.FINISHED
        self.target_number = None
        self.players = []
        self.current_player_index = 0
        self.min_range = 0
        self.max_range = 100
        self.creator_id = None
        self.round_count = 0
        self.active_group = None
        
        # 取消超时计时器
        self.cancel_timeout_timer()
    
    def set_timeout_timer(self, group_id: int):
        """设置超时计时器"""
        # 取消现有计时器
        self.cancel_timeout_timer()
        
        # 创建新计时器
        self.timeout_tasks[group_id] = asyncio.create_task(
            self.handle_timeout(group_id)
        )
    
    def cancel_timeout_timer(self):
        """取消超时计时器"""
        for task in self.timeout_tasks.values():
            task.cancel()
        self.timeout_tasks.clear()
    
    async def handle_timeout(self, group_id: int):
        """处理超时"""
        try:
            await asyncio.sleep(self.timeout_seconds)
            
            # 超时处理
            if self.game_state == GameState.PLAYING:
                timed_out_player = self.players[self.current_player_index]
                player_name = await self.get_user_name(group_id, timed_out_player)
                
                # 扣除积分
                self.update_score(timed_out_player, -1, f"第{self.round_count}轮超时")
                
                await self.send_group_message(group_id, 
                    f"⏰ {player_name} 超时！\n"
                    f"扣除 1 积分\n"
                    f"游戏继续")
                
                # 切换到下一个玩家
                self.current_player_index = (self.current_player_index + 1) % len(self.players)
                next_player_name = await self.get_user_name(group_id, self.players[self.current_player_index])
                
                await self.send_group_message(group_id, 
                    f"轮到 {next_player_name} 猜测\n"
                    f"请使用'猜 [数字]'命令猜测数字")
                
                # 重新设置超时计时器
                self.set_timeout_timer(group_id)
        except asyncio.CancelledError:
            # 计时器被取消，正常退出
            pass